import { Context, Next } from 'koa';
import { getRedisClient } from '../database/redis';
import { error } from '../utils/response';
import { ResponseCode } from '../config/responseCode';
import { consoleLog, LogType, LogLevel } from '../utils/logger';

// 超频检测配置
const FREQUENCY_WINDOW = 60; // 时间窗口：60秒
const FREQUENCY_MAX = 60; // 最大请求次数：60次
const COOLDOWN_DURATION = 60; // 冷却时间：1分钟（60秒）

/**
 * 异常检测中间件（检测超频请求）
 * 基于日志分析，检测到超频请求后，对IP进行1分钟冷却限制
 * 超频请求定义：同一个IP在同一个API路径下，60秒内请求超过60次
 */
export async function abnormalDetectionMiddleware(ctx: Context, next: Next): Promise<void> {
  const ip = ctx.ip || ctx.request.ip || 'unknown';
  const path = ctx.path || ctx.url.split('?')[0] || 'unknown';
  
  // 检查IP是否在冷却期内
  const cooldownKey = `abnormal_cooldown:${ip}`;
  // 请求计数key（基于IP和API路径）
  const countKey = `frequency_count:${ip}:${path}`;
  const redis = getRedisClient();
  
  try {
    // 先检查IP是否在冷却期内
    const isInCooldown = await redis.exists(cooldownKey);
    
    if (isInCooldown) {
      // IP在冷却期内，拒绝请求
      consoleLog(
        LogType.API,
        LogLevel.WARN,
        `IP ${ip} 在冷却期内，请求被拒绝`,
        {
          ip,
          method: ctx.method,
          url: ctx.url,
        }
      );
      error(ctx, ResponseCode.TOO_MANY_REQUESTS, '请求过于频繁，请稍后再试');
      return;
    }
    
    // 执行请求（包括后续的logger中间件）
    await next();
    
    // 请求完成后，记录请求计数（用于检测超频）
    // 注意：由于abnormalDetection在logger之前注册，执行顺序是：
    // 1. abnormalDetection的next()之前（检查冷却）
    // 2. logger的next()之前
    // 3. 路由处理
    // 4. logger的next()之后（记录日志）
    // 5. abnormalDetection的next()之后（记录计数并检测超频）
    
    // 增加请求计数
    const current = await redis.incr(countKey);
    
    if (current === 1) {
      // 第一次请求，设置过期时间
      await redis.expire(countKey, FREQUENCY_WINDOW);
    }
    
    // 检测是否超频
    if (current > FREQUENCY_MAX) {
      // 检测到超频请求，设置IP冷却期
      await redis.setex(cooldownKey, COOLDOWN_DURATION, '1');
      
      consoleLog(
        LogType.API,
        LogLevel.WARN,
        `检测到超频请求，IP ${ip} 对 ${path} 在${FREQUENCY_WINDOW}秒内请求${current}次，已被限制1分钟`,
        {
          ip,
          method: ctx.method,
          url: ctx.url,
          path,
          count: current,
          threshold: FREQUENCY_MAX,
        }
      );
    }
  } catch (err) {
    // Redis错误时，允许请求通过，避免Redis故障影响服务
    consoleLog(
      LogType.REDIS,
      LogLevel.ERROR,
      '异常检测Redis错误',
      {
        error: err,
        ip,
      }
    );
    // 如果next()还没执行，需要执行它
    if (!ctx.status) {
      await next();
    }
  }
}

